iT邦幫忙

2025 iThome 鐵人賽

DAY 6
1

在前幾天的學習中,我們已經打下了 Locust 基礎,掌握了其架構與任務管理機制。今天,讓我們將焦點轉向 Locust 的核心功能:HTTPClient。透過學習如何有效發送各種 HTTP 請求、處理不同格式的回應,以及運用 catch_response 進行自訂驗證,您將能夠建立起更全面、更貼近真實應用場景的壓力測試。

認識 HTTPClient:Locust 的強大請求工具

在 Locust 中,每個 HttpUser 實例都內建了一個 client 屬性,它是一個基於 Python requests 函式庫,並經過特別強化的 HTTP 客戶端。這個 client 不僅支援所有標準的 HTTP 方法,更重要的是,它會自動記錄每個請求的回應時間、成功率等關鍵效能指標,讓您能心無旁騖地專注於測試邏輯的撰寫。

基本 HTTP 請求一覽

無論是 GET、POST、PUT、DELETE 還是 PATCH,HTTPClient 都能輕鬆應對。以下是基本請求的程式碼範例:

from locust import HttpUser, task, between

class BasicHTTPUser(HttpUser):
    wait_time = between(1, 3)
    host = "http://localhost:8080"
    
    @task
    def test_get(self):
        # GET 請求:從伺服器獲取資源
        response = self.client.get("/products")
        
    @task
    def test_post(self):
        # POST 請求:向伺服器提交資料
        response = self.client.post("/cart/add", json={"item": "laptop"})
    
    @task
    def test_put(self):
        # PUT 請求:更新或替換伺服器上的資源
        response = self.client.put("/users/1", json={"name": "Updated Name"})
    
    @task
    def test_delete(self):
        # DELETE 請求:刪除伺服器上的資源
        response = self.client.delete("/items/1")
    
    @task
    def test_patch(self):
        # PATCH 請求:部分更新伺服器上的資源
        response = self.client.patch("/settings", json={"theme": "dark"})

深入 GET 請求:從簡單到複雜

GET 請求是我們最常用的 HTTP 方法。在 Locust 中,我們可以靈活地運用它來模擬各種瀏覽行為。

1. 基本 GET 請求與回應處理

使用 self.client.get() 方法發送請求後,我們能輕鬆檢查 HTTP 狀態碼或解析 JSON 回應。這個功能是所有測試的基石,讓您能像真實使用者一樣瀏覽網站並驗證資料。

@task
def get_products_list(self):
    """簡單的 GET 請求範例"""
    response = self.client.get("/products")
    
    # 檢查狀態碼
    if response.status_code == 200:
        print("成功獲取產品列表")
        
        # 解析 JSON 回應
        data = response.json()
        print(f"獲取到 {data['count']} 個產品")

2. 帶路徑參數的 GET 請求

當需要測試 RESTful API 時,路徑參數是不可或缺的。我們可以利用 Python 的 f-string 搭配隨機函式,來模擬使用者隨機瀏覽不同商品的行為,確保每個產品頁面都能承受壓力。

@task
def get_product_details(self):
    """使用路徑參數的 GET 請求"""
    product_id = random.choice([1, 2, 3, 4, 5])
    response = self.client.get(f"/products/{product_id}")
    
    if response.status_code == 200:
        product = response.json()
        print(f"產品名稱: {product['name']}, 價格: ${product['price']}")

3. 帶查詢參數的 GET 請求

傳遞查詢參數時,除了直接寫在 URL 中,更推薦使用 params 參數。它能自動處理 URL 編碼,避免因特殊字元而產生的問題,讓程式碼更乾淨、更可靠。

@task
def search_products_method2(self):
    """使用 params 參數(推薦)"""
    response = self.client.get(
        "/search",
        params={"q": "laptop", "category": "electronics", "sort": "price"}
    )

POST 請求:向伺服器提交資料

POST 請求主要用於向伺服器提交新資料。Locust 提供了多種方式來傳送不同格式的資料,滿足各種測試需求。

發送 JSON 與表單資料

現代 API 最常使用 JSON 格式。透過 json 參數,Locust 會自動設定 Content-Type: application/json。若要發送傳統的表單資料,則使用 data 參數即可。

@task
def add_item_to_cart(self):
    """發送 JSON 格式的資料"""
    item_data = {
        "name": "Laptop",
        "price": 1299.99,
        "quantity": 1
    }
    
    response = self.client.post(
        "/cart/add",
        json=item_data,  # 自動設定 Content-Type: application/json
        headers={"Authorization": "Bearer token123"}  # 可選的額外標頭
    )
    
@task
def submit_form(self):
    """發送表單格式的資料"""
    form_data = {
        "username": "test_user",
        "password": "password123"
    }
    
    response = self.client.post(
        "/login",
        data=form_data  # 發送 application/x-www-form-urlencoded
    )

精準掌控回應:解析、驗證與資料提取

有效的測試不僅是發出請求,更重要的是正確地解析和處理回應

1. 解析回應資料

Locust 提供了多種方法來處理不同格式的回應:

  • JSON 回應:使用 response.json() 直接將 JSON 字串轉換為 Python 字典,方便存取。
  • 純文字回應:使用 response.text 取得原始文字內容。
  • 存取回應標頭:使用 response.headers 檢查如 Content-TypeCache-Control 等重要資訊。

2. 使用 catch_response 進行自訂驗證

catch_response 是 Locust 提供的一個強大功能,讓您可以根據複雜的業務邏輯來判斷請求是否成功。它特別適用於以下情境:

  • 驗證 JSON 回應內容:檢查回傳的產品價格是否大於零、庫存是否充足。
  • 驗證回應時間:確保請求在可接受的時限內完成。
  • 處理預期的錯誤:例如,當我們預期 GET /products/999 會回傳 404 狀態碼時,可以將其視為成功案例,避免影響測試結果。
@task
def validate_with_catch_response(self):
    """使用 catch_response 進行自訂驗證"""
    with self.client.get("/products/1", catch_response=True) as response:
        if response.status_code == 200:
            product = response.json()
            
            # 自訂驗證邏輯
            if product["price"] > 0 and product["stock"] > 0:
                response.success()
            else:
                response.failure("產品價格或庫存異常")

3. 連鎖請求:從回應中提取資料

在真實世界的測試中,我們常常需要用前一個請求的回應資料來執行後續請求,這就是所謂的「連鎖請求」。這個技巧能完美模擬使用者從搜尋、瀏覽到最終購買的完整操作流程,大幅提升測試的擬真度。

@task
def chain_requests_with_data(self):
    """使用提取的資料進行連鎖請求"""
    # 步驟 1: 搜尋產品,從回應中獲取產品 ID
    search_response = self.client.get("/search?q=mouse")
    if search_response.status_code == 200:
        products = search_response.json()["products"]
        if products:
            product_id = products[0]["id"]
            
            # 步驟 2: 使用產品 ID 獲取詳情
            detail_response = self.client.get(f"/products/{product_id}")
            if detail_response.status_code == 200:
                # 步驟 3: 將產品加入購物車
                cart_response = self.client.post("/cart/add", json=cart_item)
                
                if cart_response.status_code == 200:
                    print(f"完成購買流程: 搜尋 -> 查看 -> 加入購物車")

效能優化建議:讓壓力測試更精確

  • 合理使用 catch_response:它會帶來一些額外開銷,只在需要複雜驗證時才使用。
  • 避免大量資料處理:壓力測試的重點是伺服器,而不是客戶端。避免在任務中進行複雜運算,以免影響測試結果的準確性。
  • 善用 Session 持久化:Locust 的 client 會自動維護 session 和 cookies,讓需要登入狀態的測試變得非常簡單。
  • 設定合理的超時時間:為可能較慢的 API 設定超時限制,能幫助您快速識別效能瓶頸。

總結

今天的學習讓我們對 Locust HTTPClient 有了全面的認識。我們學會了如何:

  • 發送各種基本的 HTTP 請求。
  • 傳送不同格式的資料,並處理各式各樣的回應。
  • 運用 catch_response 進行強大的自訂驗證。
  • 實作連鎖請求,模擬複雜的使用者行為。

這些技能不僅能讓您測試伺服器的效能,更能確保業務邏輯在壓力下的正確性。

明天,我們將繼續探索如何使用 Cookies 管理 Session、處理認證機制,以及實作更複雜的使用者會話管理。這些進階技巧將讓您的 Locust 測試更上一層樓,敬請期待!


上一篇
Day05 - 深入淺出 Task 以及 TaskSet 的使用
下一篇
Day07 - Locust 進階技巧:Cookie 處理與 Session 管理
系列文
Vibe Coding 後的挑戰:Locust x Loki 負載及監控13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言